/*
 * @Author: lineCode 1145938682@qq.com
 * @Date: 2025-08-28 22:42:19
 * @LastEditors: lineCode 1145938682@qq.com
 * @LastEditTime: 2025-11-09 22:01:33
 * @FilePath: /shorthand-go/dao/migration/migration.go
 */
package migration

import (
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"go.uber.org/zap"
	"gorm.io/gorm"
)

// Migration 迁移记录模型
type Migration struct {
	ID        uint      `gorm:"primarykey"`
	Version   string    `gorm:"uniqueIndex;size:20;not null"`
	Name      string    `gorm:"size:255;not null"`
	AppliedAt time.Time `gorm:"not null"`
}

// TableName 设置表名
func (Migration) TableName() string {
	return "migrations"
}

// Manager 迁移管理器
type Manager struct {
	db            *gorm.DB
	migrationsDir string
	logger        *zap.Logger
}

// NewManager 创建迁移管理器
func NewManager(db *gorm.DB, migrationsDir string, logger *zap.Logger) *Manager {
	return &Manager{
		db:            db,
		migrationsDir: migrationsDir,
		logger:        logger,
	}
}

// Init 初始化迁移表
func (m *Manager) Init() error {
	// 自动迁移迁移表结构
	if err := m.db.AutoMigrate(&Migration{}); err != nil {
		return fmt.Errorf("自动迁移迁移表失败: %v", err)
	}
	return nil
}

// GetAppliedMigrations 获取已应用的迁移
func (m *Manager) GetAppliedMigrations() (map[string]bool, error) {
	var migrations []Migration
	if err := m.db.Find(&migrations).Error; err != nil {
		return nil, fmt.Errorf("查询已应用迁移失败: %v", err)
	}

	applied := make(map[string]bool)
	for _, migration := range migrations {
		applied[migration.Version] = true
	}
	return applied, nil
}

// GetMigrationFiles 获取迁移文件列表
func (m *Manager) GetMigrationFiles() ([]string, error) {
	files, err := os.ReadDir(m.migrationsDir)
	if err != nil {
		return nil, fmt.Errorf("读取迁移目录失败: %v", err)
	}

	var migrationFiles []string
	for _, file := range files {
		if !file.IsDir() && strings.HasSuffix(file.Name(), ".sql") && !strings.Contains(file.Name(), "rollback_") {
			migrationFiles = append(migrationFiles, file.Name())
		}
	}

	// 按版本号排序
	sort.Strings(migrationFiles)
	return migrationFiles, nil
}

// Migrate 执行数据库迁移
func (m *Manager) Migrate() error {
	// 初始化迁移表
	if err := m.Init(); err != nil {
		return err
	}

	// 获取已应用的迁移
	applied, err := m.GetAppliedMigrations()
	if err != nil {
		return err
	}

	// 获取迁移文件
	migrationFiles, err := m.GetMigrationFiles()
	if err != nil {
		return err
	}

	// 执行未应用的迁移
	for _, fileName := range migrationFiles {
		// 提取版本号（假设文件名格式为: V1__description.sql）
		parts := strings.SplitN(fileName, "__", 2)
		if len(parts) < 2 {
			m.logger.Warn("跳过无效的迁移文件名", zap.String("file", fileName))
			continue
		}

		version := strings.TrimPrefix(parts[0], "V")
		if applied[version] {
			continue // 已应用，跳过
		}

		// 读取SQL文件内容
		sqlPath := filepath.Join(m.migrationsDir, fileName)
		sqlContent, err := os.ReadFile(sqlPath)
		if err != nil {
			return fmt.Errorf("读取迁移文件失败 %s: %v", fileName, err)
		}

		// 开始事务
		tx := m.db.Begin()

		// 执行SQL
		if err := tx.Exec(string(sqlContent)).Error; err != nil {
			tx.Rollback()
			return fmt.Errorf("执行迁移失败 %s: %v", fileName, err)
		}

		// 记录迁移记录
		migration := Migration{
			Version:   version,
			Name:      strings.TrimSuffix(parts[1], ".sql"),
			AppliedAt: time.Now(),
		}

		if err := tx.Create(&migration).Error; err != nil {
			tx.Rollback()
			return fmt.Errorf("记录迁移失败 %s: %v", fileName, err)
		}

		// 提交事务
		if err := tx.Commit().Error; err != nil {
			return fmt.Errorf("提交迁移事务失败 %s: %v", fileName, err)
		}

		m.logger.Info("迁移成功应用", zap.String("version", version), zap.String("name", migration.Name))
	}

	m.logger.Info("数据库迁移完成")
	return nil
}

// Rollback 回滚最近一次迁移
func (m *Manager) Rollback() error {
	// 获取最近一次应用的迁移
	var lastMigration Migration
	result := m.db.Order("applied_at desc").First(&lastMigration)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return fmt.Errorf("没有找到可回滚的迁移")
		}
		return fmt.Errorf("查询最后一次迁移失败: %v", result.Error)
	}

	// 查找对应的回滚文件（假设回滚文件名为: V<version>__rollback_<name>.sql）
	rollbackFileName := fmt.Sprintf("V%s__rollback_%s.sql", lastMigration.Version, lastMigration.Name)
	rollbackPath := filepath.Join(m.migrationsDir, rollbackFileName)

	// 检查回滚文件是否存在
	if _, err := os.Stat(rollbackPath); os.IsNotExist(err) {
		return fmt.Errorf("未找到回滚文件: %s", rollbackFileName)
	}

	// 读取回滚SQL内容
	rollbackSQL, err := os.ReadFile(rollbackPath)
	if err != nil {
		return fmt.Errorf("读取回滚文件失败: %v", err)
	}

	// 开始事务
	tx := m.db.Begin()

	// 执行回滚SQL
	if err := tx.Exec(string(rollbackSQL)).Error; err != nil {
		tx.Rollback()
		return fmt.Errorf("执行回滚失败: %v", err)
	}

	// 删除迁移记录
	if err := tx.Where("version = ?", lastMigration.Version).Delete(&Migration{}).Error; err != nil {
		tx.Rollback()
		return fmt.Errorf("删除迁移记录失败: %v", err)
	}

	// 提交事务
	if err := tx.Commit().Error; err != nil {
		return fmt.Errorf("提交回滚事务失败: %v", err)
	}

	m.logger.Info("数据库回滚成功", zap.String("version", lastMigration.Version), zap.String("name", lastMigration.Name))
	return nil
}

// RollbackAll 回滚所有迁移
func (m *Manager) RollbackAll() error {
	// 获取所有已应用的迁移（按应用时间倒序）
	var migrations []Migration
	if err := m.db.Order("applied_at desc").Find(&migrations).Error; err != nil {
		return fmt.Errorf("查询已应用迁移失败: %v", err)
	}

	if len(migrations) == 0 {
		return fmt.Errorf("没有找到可回滚的迁移")
	}

	for _, migration := range migrations {
		// 查找对应的回滚文件
		rollbackFileName := fmt.Sprintf("V%s__rollback_%s.sql", migration.Version, migration.Name)
		rollbackPath := filepath.Join(m.migrationsDir, rollbackFileName)

		// 检查回滚文件是否存在
		if _, err := os.Stat(rollbackPath); os.IsNotExist(err) {
			return fmt.Errorf("未找到回滚文件: %s", rollbackFileName)
		}

		// 读取回滚SQL内容
		rollbackSQL, err := os.ReadFile(rollbackPath)
		if err != nil {
			return fmt.Errorf("读取回滚文件失败 %s: %v", rollbackFileName, err)
		}

		// 开始事务
		tx := m.db.Begin()

		// 执行回滚SQL
		if err := tx.Exec(string(rollbackSQL)).Error; err != nil {
			tx.Rollback()
			return fmt.Errorf("执行回滚失败 %s: %v", migration.Version, err)
		}

		// 删除迁移记录
		if err := tx.Where("version = ?", migration.Version).Delete(&Migration{}).Error; err != nil {
			tx.Rollback()
			return fmt.Errorf("删除迁移记录失败 %s: %v", migration.Version, err)
		}

		// 提交事务
		if err := tx.Commit().Error; err != nil {
			return fmt.Errorf("提交回滚事务失败 %s: %v", migration.Version, err)
		}

		m.logger.Info("数据库回滚成功", zap.String("version", migration.Version), zap.String("name", migration.Name))
	}

	m.logger.Info("所有数据库迁移已回滚")
	return nil
}
